var content = '<style type="text/css"> \
  .content {position:absolute;left:0;top:0;width:100%;height:100%;} \
  .statusTop {background-color: #eeeea6; padding:10px; font-size: 8pt; } \
  .timelinearea {visibility: visible; display: block;  } \
  .popupon  { visibility: visible; background-color: #f0f6f6; border: 1px solid #000000; position: fixed; top: 9; left: 5; z-index: 1000; width: 330px; font-size: 8pt; } \
  .popupoff { visibility: hidden;  background-color: #f0f6f6; border: 1px solid #000000; position: fixed; top: 9; left: 5; z-index: 1000; width: 330px; font-size: 8pt; } \
  .clicker  { font-size: 8pt; background-color: #eeeeee; } \
  .innertitle { font-size: 10pt; font-weight: bold; } \
  .helpheader { font-size: 10pt; text-decoration:underline;} \
  .helptext { font-size: 8pt; } \
  .sectionhead { font-size: 9pt; font-weight: bold; } \
  body {font-family: verdana; font-size: small;} \
</style>';
document.write(content);

var Timeline_ajax_url ="http://api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js";
var Timeline_urlPrefix="http://api.simile-widgets.org/timeline/2.3.1/";       
var Timeline_parameters="bundle=false";
content = "<script src='http://www.google.com/jsapi' type='text/javascript'></script>";
document.write(content);
content = "<script src='http://api.simile-widgets.org/timeline/2.3.1/timeline-api.js' type='text/javascript'></script>";
document.write(content);

content = '<div class="content"> \
  <div id="helpbox" class="popupoff"> \
    <div style="width: 100%;"> \
      <table><tr> \
        <td width="75%" id="helpHeadText" class="helpheader">How to use this TimeLine Gadget... </td> \
        <td width="25%" style="text-align: right;"> \
          <span class="clicker" style="text-align:right; "><a href="#" onClick="helpPop();">close</a></span> \
        </td></tr> \
      </table> \
    </div> \
    <span class="helptext"> \
      This gadget is used within a Google spreadsheet to create a visual timeline \
      of dated information. The source spreadsheet must contain the following fields:<br/> \
      <ul class="helptext" style="list-style-type: square;"> \
        <li><span class="sectionhead">title:</span> description of each event</li> \
        <li><span class="sectionhead">start:</span> the start date of the event<br/> \
        <li><span class="sectionhead">end:</span> the end date of the event (optional)<br/> \
      </ul> \
      Controls:<br/> \
      <ul class="helptext" style="list-style-type: square;"> \
        <li><span class="sectionhead">scroll:</span> pgUp, pgDn, home, end, left & right arrow keys, mouse pan or mouse wheel on 2nd or 3rd band</li> \
        <li><span class="sectionhead">zoom:</span> up & down arrow keys, mouse wheel on top band<br/> \
        <li><span class="sectionhead">view:</span> click on item<br/> \
      </ul> \
      <p>Timeline version <span id="tl_ver"></span>.\
      <span style="text-align:right;"> \
      <a href="http://code.google.com/p/x-timeline/wiki/Home" target="_blank">more help...</a> \
    </span> \
  </div> \
  <div id="status" style="height: 4%; min-height: 18px; "> \
    <table style="border-collapse: collapse; border: 0px; width: 100%;"> \
      <tr> \
        <td id="statusLeft" width="10%" style=""> \
          <a href="#" id="helpButton" class="statusTop" onClick="helpPop();">help</a> \
        </td> \
        <td id="statusTitle" class="innertitle" width="80%" style="text-align: center;"></td> \
        <td id="statusRight" width="10%" style="text-align: right;"> \
          <a href="" id="newWinLink" class="statusTop" onClick="popOut();">Pop-out</a> \
        </td> \
      </tr> \
    </table> \
  </div> \
  <div id="timelinebox"  style="height: 75%; min-height: 500px;">Loading...</div> \
  <div class="controls" id="controls"></div> \
</div> \
';
document.write(content);

var undefinedText = 'undefined';
// [JR] Respond to HELP BUTTON click
function helpPop() {
	var helpDiv = document.getElementById("helpbox");
	if(helpDiv.className == 'popupoff') {
		helpDiv.className = "popupon";
	} else {
		helpDiv.className = "popupoff";
	}
}

// Open the gadget in its own browser window   
function popOut() {
	window.open(window.location.href, prefs.getString("tltitle"));
}              
		
function _getBaseURL(url) {
    if (url.indexOf("://") < 0) {
        var url2 = _getBaseURL(document.location.href);
        if (url.substr(0,1) == "/") {
            url = url2.substr(0, url2.indexOf("/", url2.indexOf("://") + 3)) + url;
        } else {
            url = url2 + url;
        }
    }
    
    var i = url.lastIndexOf("/");
    if (i < 0) {
        return "";
    } else {
        return url.substr(0, i+1);
    }
}

function _resolveRelativeURL(url, base) {
    if (url == null || url == "") {
        return url;
    } else if (url.indexOf("://") > 0) {
        return url;
    } else if (url.substr(0,1) == "/") {
        return base.substr(0, base.indexOf("/", base.indexOf("://") + 3)) + url;
    } else {
        return base + url;
    }
}
    
function _getColorFromCountry(country) {
	var colorMap = new Array();
	colorMap['china'] = '#FF0000';
	colorMap['japan'] = '#dddd00';
	colorMap['usa']   = '#FF00FF';
	colorMap['europe']= '#00DDDD';
  
  return ((country !=null) && (country.toLowerCase() in colorMap))? colorMap[country.toLowerCase()]:'#000000';
}

function _getTrackFromCountry(country) {
	var trackMap = new Array();
	trackMap['china'] = 1;
	trackMap['japan'] = 2;
	trackMap['usa']   = 3;
	trackMap['europe']= 4;
  
  return ((country !=null) && (country.toLowerCase() in trackMap))? trackMap[country.toLowerCase()]:null;
}

function buildEvents(sheetData) {
  // helper object - array without duplicates
  function arr() {
    this._data = new Array();
    this.add = function(el) {
      //if (el==null) return -1;
      for (var i=0; i<this._data.length; i++) {
        if (this._data[i] == el) // this[i]===v || strict && this[i]==v
          return -1;
      }
      return this._data.push(el);
    }
    this.getArray = function() {
      return this._data;
    }
  };
  types = new arr();
  countries = new arr();
  
	var nullF = function() { return null; };
	// [JR] added column position (colpos) to indicate whether/where the required column was found (-1 is not found)
	var columnMap = {
			"title" :       { accessor: nullF, format: "string", colpos: -1 },
			"start" :       { accessor: nullF, format: "date",   colpos: -1 },
			"end" :         { accessor: nullF, format: "date",   colpos: -1 },
			"description" : { accessor: nullF, format: "string", colpos: -1 },
			"link" :        { accessor: nullF, format: "string", colpos: -1 },
			"image" :       { accessor: nullF, format: "string", colpos: -1 },
			"icon" :        { accessor: nullF, format: "string", colpos: -1 },
			"lateststart" : { accessor: nullF, format: "date",   colpos: -1 },
			"earliestend" : { accessor: nullF, format: "date",   colpos: -1 },
			"tapeimage" :   { accessor: nullF, format: "string", colpos: -1 },
			"wikiurl"     : { accessor: nullF, format: "string", colpos: -1 },
			"wikisection" : { accessor: nullF, format: "string", colpos: -1 },      
      
			"isera" :       { accessor: nullF, format: "bool",   colpos: -1 },
			"country" :     { accessor: nullF, format: "string", colpos: -1 },
			"type" :        { accessor: nullF, format: "string", colpos: -1 }
	};
	var makeAccessor = function(c, format) {
			switch (format) {
			case "string":
					return function(r) {
							return sheetData.getFormattedValue(r, c);
					};
			case "date":
					return function(r) {
							var v = sheetData.getValue(r, c);
							return (v != null && typeof v != "string") ? v : null;
					};
			case "bool":
					return function(r) {
							var v = sheetData.getValue(r, c);
							return (v != null && typeof v == "boolean") ? v : false;
					};
			}
			return nullF;
	}
	var colName = new Array();   // [JR] array to save column labels of all columns in the source range
	var colCount = sheetData.getNumberOfColumns();
	for (var col = 0; col < colCount; col++) {
    var label = sheetData.getColumnLabel(col).toLowerCase();
    colName[col] = label;  // save the label for later flexibility to give user more options for mapping
    if (label in columnMap) {
      columnMap[label].accessor = makeAccessor(col, columnMap[label].format);
      columnMap[label].colpos = col;  // now we know in which column each required field was found
    } 
	}
	// [JR] check all columns required to count how many are found
	var countFound = 0;
	countFound += (columnMap["title"].colpos >= 0) ? 1 : 0;
	countFound += (columnMap["start"].colpos >= 0) ? 1 : 0;
	countFound += (columnMap["end"].colpos >= 0) ? 1 : 0;
			
	if (countFound < 3) {
		alert("You may not have included all the proper column headers in your range - please click help in the timeline");
	}

	var baseURL = _getBaseURL(document.location.href);
	var events = ['eraSource', 'eventsSource', 'types', 'countries'];
	events['eraSource'] = [];;
	events['eventsSource'] = [];  
	events['types'] = [];  
	events['countries'] = [];  
  
	for (var row = 0; row < sheetData.getNumberOfRows(); row++) {
			var start = columnMap["start"].accessor(row);
			var end = columnMap["end"].accessor(row);
			var latestStart = columnMap["lateststart"].accessor(row);
			var earliestEnd = columnMap["earliestend"].accessor(row);
      var isEra = columnMap["isera"].accessor(row);
			// [JR] Inserted logic to handle missing start dates - either set the start date to be the end date or skip the row
			if(!start) {
				if(!end) {
					break;  // leave this loop to get the next row, since no start date is fatal
				} else {
					start = end;  // if there's an end date, but no start date - just use the end date  
				}
			}
			
      // put each valid event on the timeline
			var A = {
				id: null, // internal id - don't touch it
				start: start,
				latestStart: latestStart,
				earliestEnd: earliestEnd,
				end: end,
				instant:	(end == null), // instant?
				text: columnMap["title"].accessor(row),
				description: columnMap["description"].accessor(row),
				image: _resolveRelativeURL(columnMap["image"].accessor(row), baseURL),
				link: _resolveRelativeURL(columnMap["link"].accessor(row), baseURL),
				icon: _resolveRelativeURL(columnMap["icon"].accessor(row) , baseURL),
				color: _getColorFromCountry(columnMap["country"].accessor(row)), 
				textColor: null,
				classname: null,
				tapeImage: _resolveRelativeURL(columnMap["tapeimage"].accessor(row), baseURL),
				tapeRepeat: null,
				caption: columnMap["title"].accessor(row),
				eventID: "event" + row, // ID,
				trackNum: (isEra?_getTrackFromCountry(columnMap["country"].accessor(row)):null),
        
        country: columnMap["country"].accessor(row),
        type: (isEra?null:columnMap["type"].accessor(row)),
        srcLineNr: (row + 1)
			};
			var L = new Timeline.DefaultEventSource.Event(A);
			L._obj = A;
			L.getProperty = function(M){return this._obj[M];};

      wikiURL = columnMap["wikiurl"].accessor(row);
      wikiSection = columnMap["wikisection"].accessor(row);
			L.setWikiInfo(wikiURL, wikiSection);
			
      if (isEra) {
        events['eraSource'].push(L);
      } else {
        events['eventsSource'].push(L);
      }
      
    countries.add(columnMap["country"].accessor(row));
    types.add(columnMap["type"].accessor(row));
	}
	events['types'] = types.getArray();  
	events['countries'] = countries.getArray();  
	return events;	
}

function buildSettings(userPrefs) {
		// [JR] values to associate to userpref for width of each interval
/* not used
		var widthValue = {
			"xx-narrow" : 25,
			"x-narrow" : 50,
			"narrow" : 100,
			"medium" : 200,
			"wide" : 300,
			"x-wide" : 500,
			"xx-wide" : 1000
		};
*/
	var settings = {
//		band1int: 'week',
//		band2int: 'month',
//		upperWidth: widthValue["narrow"],
//		lowerWidth: widthValue["x-narrow"],
		statusTitle: "tl title"   // set timeline title
//		backgroundColorArray: ["#ddeeff", "#ddeeff", "#ddeeff", "#ddeeff"]		
	};
/*
	try {
		// [JR] Get each band interval from the user prefs
		var band1int = Timeline.DateTime.WEEK;
		if(prefs.getString("band1interval") == 'month') {
				band1int = Timeline.DateTime.MONTH;
		} else {
			if(prefs.getString("band1interval") == 'year') {
				band1int = Timeline.DateTime.YEAR;
			}
		}
		var band2int = Timeline.DateTime.MONTH;
		if(prefs.getString("band2interval") == 'year') {
				band2int = Timeline.DateTime.YEAR;
		} else {
			if(prefs.getString("band2interval") == 'decade') {
				band2int = Timeline.DateTime.DECADE;
			}
		}

		// [JR] Use UserPrefs to set other display visuals = = = = = =  
		document.getElementById("statusTitle").innerHTML = prefs.getString("tltitle");   // set timeline title
		var backgroundColorArray = new Array();
		backgroundColorArray = [prefs.getString("bc1"), prefs.getString("bc2"), prefs.getString("bc3"), prefs.getString("bc4")];
		var upperWidth = widthValue[prefs.getString("band1width")];
		var lowerWidth = widthValue[prefs.getString("band2width")];
	} catch (e) {
			alert('Unable to apply settings: ' + e);
	}
	*/
	return settings;
}

function createTimeline(events, settings) {
	document.getElementById("statusTitle").innerHTML = settings.statusTitle;   // set timeline title

  Timeline._Band.prototype._onKeyDown = function(keyboardInput, evt, target) {
      if (!this._dragging) {
          switch (evt.keyCode) {
          case 27: // ESC
              break;
          case 37: // left arrow
              this._scrollSpeed = Math.min(50, Math.abs(this._scrollSpeed * 1.05));
              this._moveEther(this._scrollSpeed);
              break;
          case 39: // right arrow
              this._scrollSpeed = -Math.min(50, Math.abs(this._scrollSpeed * 1.05));
              this._moveEther(this._scrollSpeed);
              break;
          case 40: // down arrow
              var middlePix = -1*this._viewOffset + this._viewLength / 2;
              this._timeline.zoom(false, middlePix, 0, this._div);
              break;  
          case 38: // up arrow
              var middlePix = -1*this._viewOffset + this._viewLength / 2;
              this._timeline.zoom(true, middlePix, 0, this._div);
              break;  
          default:
              return true;
          }
          this.closeBubble();
          
          SimileAjax.DOM.cancelEvent(evt);
          return false;
      }
      return true;
  };
// - - - - - - - - - - - - - - - - - - - - -  [JR] REPLACING FUNCTION TO FORCE LINKS TO OPEN IN A NEW WINDOW
	Timeline.DefaultEventSource.Event.prototype.fillInfoBubble = function(elmt, theme, labeller) {
    var doc = elmt.ownerDocument;
    
    var title = this.getText();
    var link = this.getLink();
    var image = this.getImage();
    
    if (image != null) {
        var img = doc.createElement("img");
        img.src = image;
        
        theme.event.bubble.imageStyler(img);
        elmt.appendChild(img);
    }
    
    var divTitle = doc.createElement("div");
    var textTitle = doc.createTextNode(title);
    if (link != null) {
        var a = doc.createElement("a");
        a.href = link;
			a.target = "_blank";
        a.appendChild(textTitle);
        divTitle.appendChild(a);
    } else {
        divTitle.appendChild(textTitle);
    }
    theme.event.bubble.titleStyler(divTitle);
    elmt.appendChild(divTitle);
    
    var divBody = doc.createElement("div");
    this.fillDescription(divBody);
    theme.event.bubble.bodyStyler(divBody);
    elmt.appendChild(divBody);
    
    var divTime = doc.createElement("div");
    this.fillTime(divTime, labeller);
    theme.event.bubble.timeStyler(divTime);
    elmt.appendChild(divTime);

    var divSrcLineNr = doc.createElement("div");
    divSrcLineNr.innerHTML = 'Source line # ' + this.getProperty('srcLineNr');
    theme.event.bubble.timeStyler(divSrcLineNr);
    elmt.appendChild(divSrcLineNr);
    
    var divWiki = doc.createElement("div");
    this.fillWikiInfo(divWiki);
    theme.event.bubble.wikiStyler(divWiki);
    elmt.appendChild(divWiki);
	}
	// - - - - - - - - - - - - - - - - - - - - -     

	var eventSourceEvents = new Timeline.DefaultEventSource();
	var eventSourceEra = new Timeline.DefaultEventSource();

	var themeEvents = Timeline.ClassicTheme.create();
	//themeEvents.autoWidth = true; // Set the Timeline's "width" automatically.
													 // Set autoWidth on the Timeline's first band's theme,
													 // will affect all bands.
	themeEvents.timeline_start = new Date(Date.UTC(-3000, 0, 1));
	themeEvents.timeline_stop  = new Date(Date.UTC(3000, 0, 1));
	
	// create a second theme for the second band because we want it to
	// have different settings
	var themeEra = Timeline.ClassicTheme.create();
	// increase tape height
	themeEra.event.tape.height = 6; // px
	themeEra.event.track.height = themeEra.event.tape.height + 6;
	var d = Timeline.DateTime.parseGregorianDateTime("1900")

	try {
		var bandInfos = [
				Timeline.createBandInfo({
					eventSource: eventSourceEvents,
					date:           d,
					width:          "60%", 
					theme: 					themeEvents,
					intervalUnit:   Timeline.DateTime.YEAR, 
					intervalPixels: 100,
					zoomIndex:      3,
					zoomSteps:      new Array(
 						{pixelsPerInterval: 200,  unit: Timeline.DateTime.DECADE},
 						{pixelsPerInterval: 050,  unit: Timeline.DateTime.YEAR},
						{pixelsPerInterval: 100,  unit: Timeline.DateTime.YEAR}, 
						{pixelsPerInterval: 200,  unit: Timeline.DateTime.YEAR}
					),
					layout:         'detailed'  // original, overview, detailed
				}),
				Timeline.createBandInfo({
					eventSource: eventSourceEvents,
					date:           d,
					width:          "20%", 
					theme: 					themeEvents,
					intervalUnit:   Timeline.DateTime.DECADE, 
					intervalPixels: 100,
					layout:         'overview'  // original, overview, detailed
				}),
				Timeline.createBandInfo({
					eventSource: eventSourceEra,
					date:           d,
					width:          "20%", 
					theme: 					themeEra,
					intervalUnit:   Timeline.DateTime.CENTURY, 
					intervalPixels: 50,
					layout:         'original'  // original, overview, detailed
				})
		];
		bandInfos[1].syncWith = 0;
		bandInfos[2].syncWith = 0;
		bandInfos[2].highlight = true;
		bandInfos[1].highlight = true;
	} catch (e) {
			alert('Unable to create bandInfos: ' + e);
	}
	try {
		window.tl = Timeline.create(document.getElementById("timelinebox"), bandInfos, Timeline.HORIZONTAL);
	} catch (e) {
			alert('Unable to create timeline: ' + e);
	}
	
	try {
		eventSourceEvents.addMany(events['eventsSource']);
		eventSourceEra.addMany(events['eraSource']);
	} catch (e) {
			alert('Unable to add events to timeline: ' + e);
	}
  setupFilterHighlightControls(document.getElementById("controls"), tl, [0,1], themeEvents, events['countries'], events['types']);
  
}

function centerSimileAjax(date) {
    tl.getBand(0).setCenterVisibleDate(SimileAjax.DateTime.parseGregorianDateTime(date));
}

function setupFilterHighlightControls(div, timeline, bandIndices, theme, countries, types) {
    var table = document.createElement("table");
    var tr = table.insertRow(0);
    
    var td = tr.insertCell(0);
    td.innerHTML = "<div class='sectionhead'>Filter:</div>";
    
    td = tr.insertCell(1);
    td.innerHTML = "<div class='sectionhead'>Highlight:</div>";
    
    var handler = function(elmt, evt, target) {
        onKeyPress(timeline, bandIndices, table);
    };
    
    tr = table.insertRow(1);
    tr.style.verticalAlign = "top";
    
    td = tr.insertCell(0);
    
    var input = document.createElement("input");
    input.type = "text";
    SimileAjax.DOM.registerEvent(input, "keypress", handler);
    td.appendChild(input);
    
    for (var i = 0; i < theme.event.highlightColors.length; i++) {
        td = tr.insertCell(i + 1);
        
        input = document.createElement("input");
        input.type = "text";
        SimileAjax.DOM.registerEvent(input, "keypress", handler);
        td.appendChild(input);
        
        var divColor = document.createElement("div");
        divColor.style.height = "0.5em";
        divColor.style.background = theme.event.highlightColors[i];
        td.appendChild(divColor);
    }
    
    td = tr.insertCell(tr.cells.length);
    var button = document.createElement("button");
    button.innerHTML = "Clear All";
    SimileAjax.DOM.registerEvent(button, "click", function() {
        clearAll(timeline, bandIndices, table);
    });
    td.appendChild(button);

    // checkboxes for countries & types   
    tr = table.insertRow(2);    
    td = tr.insertCell(0);
    td.innerHTML = "<div class='sectionhead'>Show only:</div>";
    
    tr = table.insertRow(3);
    tr.style.verticalAlign = "center";
    tr.style.fontSize = "12px";
    countries.sort();
    for (var i = 0; i < countries.length; i++) {
      td = tr.insertCell(i);
 
      input = null;
      input = document.createElement("div");
      input.innerHTML = "<input type='checkbox' name='countries' value='1' checked='true'><span>"+ ((countries[i]==null)?undefinedText:countries[i]) +"</span>";
      input.style.backgroundColor = _getColorFromCountry(countries[i]);
      SimileAjax.DOM.registerEvent(input.firstChild, "click", handler);

      td.appendChild(input);
    }
    tr = table.insertRow(4);
    tr.style.verticalAlign = "center";
    tr.style.fontSize = "12px";
    types.sort();
    for (var i = 0; i < types.length; i++) {
      td = tr.insertCell(i);
 
      input = null;
      input = document.createElement("div");
      input.innerHTML = "<input type='checkbox' name='types' value='1' checked='true'><span>" + ((types[i]==null)?undefinedText:types[i]) + "</span>";
      SimileAjax.DOM.registerEvent(input.firstChild, "click", handler);

      td.appendChild(input);
    }
    
    div.appendChild(table);
}

var timerID = null;
function onKeyPress(timeline, bandIndices, table) {
    if (timerID != null) {
        window.clearTimeout(timerID);
    }
    timerID = window.setTimeout(function() {
        performFiltering(timeline, bandIndices, table);
    }, 300);
}

function cleanString(s) {
    return s.replace(/^\s+/, '').replace(/\s+$/, '');
}

function performFiltering(timeline, bandIndices, table) {
    timerID = null;
    
    var tr = table.rows[1];
    var text = cleanString(tr.cells[0].firstChild.value);
    
    // get countries
    allowedCountries = new Array();
    tr = table.rows[3];
    var td = tr.firstChild;
    while (td != null) {
      if (td.firstChild.childNodes[0].checked) {
        allowedCountries.push(td.firstChild.childNodes[1].innerHTML);
      }
      td = td.nextSibling;
    }
    
    // get types
    allowedTypes = new Array();
    tr = table.rows[4];
    var td = tr.firstChild;
    while (td != null) {
      if (td.firstChild.childNodes[0].checked)
        allowedTypes.push(td.firstChild.childNodes[1].innerHTML);
      td = td.nextSibling;
    }
    
    var filterMatcher = null;
    if (text.length > 0 || allowedCountries.length < this.countries.getArray().length || allowedTypes.length < this.types.getArray().length) {
        var regex = (text.length > 0)?new RegExp(text, "i"):null;
        filterMatcher = function(evt) {
          var textPass = (regex != null)?(regex.test(evt.getText()) || regex.test(evt.getDescription())):true;
          c = evt.getProperty('country');
          var countryPass = (c != null)?(this.allowedCountries.indexOf(c) >= 0):(this.allowedCountries.indexOf(undefinedText) >= 0);
          t = evt.getProperty('type');
          var typePass = (t != null)?(this.allowedTypes.indexOf(t) >= 0):(this.allowedTypes.indexOf(undefinedText) >= 0);
          return textPass && countryPass && typePass; 
        };
    }
    
    tr = table.rows[1];
    var regexes = [];
    var hasHighlights = false;
    for (var x = 1; x < tr.cells.length - 1; x++) {
        var input = tr.cells[x].firstChild;
        var text2 = cleanString(input.value);
        if (text2.length > 0) {
            hasHighlights = true;
            regexes.push(new RegExp(text2, "i"));
        } else {
            regexes.push(null);
        }
    }
    var highlightMatcher = hasHighlights ? function(evt) {
        var text = evt.getText();
        var description = evt.getDescription();
        for (var x = 0; x < regexes.length; x++) {
            var regex = regexes[x];
            if (regex != null && (regex.test(text) || regex.test(description))) {
                return x;
            }
        }
        return -1;
    } : null;
    
    for (var i = 0; i < bandIndices.length; i++) {
        var bandIndex = bandIndices[i];
        timeline.getBand(bandIndex).getEventPainter().setFilterMatcher(filterMatcher);
        timeline.getBand(bandIndex).getEventPainter().setHighlightMatcher(highlightMatcher);
    }
    timeline.paint();
}

function clearAll(timeline, bandIndices, table) {
    var tr = table.rows[1];
    for (var x = 0; x < tr.cells.length - 1; x++) {
        tr.cells[x].firstChild.value = "";
    }
    
    allowedCountries = new Array();
    tr = table.rows[3];
    var td = tr.firstChild;
    while (td != null) {
      td.firstChild.childNodes[0].checked=true;
      td = td.nextSibling;
    }
    
    // get types
    allowedTypes = new Array();
    tr = table.rows[4];
    var td = tr.firstChild;
    while (td != null) {
      td.firstChild.childNodes[0].checked=true;
      td = td.nextSibling;
    }
    
    for (var i = 0; i < bandIndices.length; i++) {
        var bandIndex = bandIndices[i];
        timeline.getBand(bandIndex).getEventPainter().setFilterMatcher(null);
        timeline.getBand(bandIndex).getEventPainter().setHighlightMatcher(null);
    }
    timeline.paint();
}
